
int isGame(JNIEnv *env, jstring appDataDir, const char* game_name) {
    if (!appDataDir)
        return 0;

    const char *app_data_dir = env->GetStringUTFChars(appDataDir, nullptr);

    int user = 0;
    static char package_name[256];
    if (sscanf(app_data_dir, "/data/%*[^/]/%d/%s", &user, package_name) != 2) {
        if (sscanf(app_data_dir, "/data/%*[^/]/%s", package_name) != 1) {
            package_name[0] = '\0';
            LOGW("can't parse %s", app_data_dir);
            return 0;
        }
    }
    env->ReleaseStringUTFChars(appDataDir, app_data_dir);
    if (strcmp(package_name, game_name) == 0) {
        LOGD("detect game: %s", package_name);
        return 1;
    }
    else {
        return 0;
    }
}

/*
 * 获取一个模块在内存中载入的 基址.
 */
unsigned long get_module_base(const char* module_name)
{
    FILE *fp;
    unsigned long addr = 0;
    char *pch;
    char filename[32];
    char line[1024];

    snprintf(filename, sizeof(filename), "/proc/self/maps");

    fp = fopen(filename, "r");

    if (fp != nullptr) {
        while (fgets(line, sizeof(line), fp)) {
            if (strstr(line, module_name) && strstr(line, "r-xp")) {
                libil2cpp_file_path = strrchr(line, ' ') + 1;
                // LOGD("app file path: %s", app_file_path);
                pch = strtok(line, "-");
                addr = strtoul(pch, nullptr, 16);
                if (addr == 0x8000)
                    addr = 0;
                break;
            }
        }
        fclose(fp);
    }
    return addr;
}

/*
 * 将 il2cpp 里使用的 unicode 编码的字符串转换为 utf8 字符串.
 * utf8 中一个字符可能有 1~6 个字节长...
 */
int unicode2UTF(long unic, char *pOutput)
{
    if (unic >= 0xFFFF0000)
        unic %= 0xFFFF0000;
    if (unic <= 0x0000007F)
    {
        *pOutput = (unic & 0x7F);
        return 1;
    }
    else if (unic >= 0x00000080 && unic <= 0x000007FF)
    {
        *(pOutput + 1) = (unic & 0x3F) | 0x80;
        *pOutput = ((unic >> 6) & 0x1F) | 0xC0;
        return 2;
    }
    else if (unic >= 0x00000800 && unic <= 0x0000FFFF)
    {
        *(pOutput + 2) = (unic & 0x3F) | 0x80;
        *(pOutput + 1) = ((unic >> 6) & 0x3F) | 0x80;
        *pOutput = ((unic >> 12) & 0x0F) | 0xE0;
        return 3;
    }
    else if (unic >= 0x00010000 && unic <= 0x001FFFFF)
    {
        *(pOutput + 3) = (unic & 0x3F) | 0x80;
        *(pOutput + 2) = ((unic >> 6) & 0x3F) | 0x80;
        *(pOutput + 1) = ((unic >> 12) & 0x3F) | 0x80;
        *pOutput = ((unic >> 18) & 0x07) | 0xF0;
        return 4;
    }
    else if (unic >= 0x00200000 && unic <= 0x03FFFFFF)
    {
        *(pOutput + 4) = (unic & 0x3F) | 0x80;
        *(pOutput + 3) = ((unic >> 6) & 0x3F) | 0x80;
        *(pOutput + 2) = ((unic >> 12) & 0x3F) | 0x80;
        *(pOutput + 1) = ((unic >> 18) & 0x3F) | 0x80;
        *pOutput = ((unic >> 24) & 0x03) | 0xF8;
        return 5;
    }
    else if (unic >= 0x04000000 && unic <= 0x7FFFFFFF)
    {
        *(pOutput + 5) = (unic & 0x3F) | 0x80;
        *(pOutput + 4) = ((unic >> 6) & 0x3F) | 0x80;
        *(pOutput + 3) = ((unic >> 12) & 0x3F) | 0x80;
        *(pOutput + 2) = ((unic >> 18) & 0x3F) | 0x80;
        *(pOutput + 1) = ((unic >> 24) & 0x3F) | 0x80;
        *pOutput = ((unic >> 30) & 0x01) | 0xFC;
        return 6;
    }

    return 0;
}

/*
 * 将 src 字符串里的 start 位开始的 len 个字符复制到 dst 字符串中去.
 * dst 需要先分配好空间.
 */
char *substring(char *src, int start, int len, char *dst) {
    char *p = dst;
    char *q = src;
    int length = strlen(src);
    if (start >= length || start < 0) return NULL;
    // 避免超出
    if (len > length - start) len = length - start;
    q += start;
    while (len--) {
        *(p++) = *(q++);
    }
    *p = '\0';
    return dst;
}

/*
 * 获取子字符串出现的次数.
 */
int get_substring_count(char *str, char *find){
    int x=0;
    char *p;//任意附个初始值
    do{
        p = strstr(str, find);//1.p指针指向strstr的返回值。3.再一次循环到 这里函数的参数发生变化，p重新指向strstr返回值，如此循环。
        if(p != NULL) {
            str=p+1;//2.str同样指向strstr返回值p的下一个地址。
            x++;
        }
    } while(p!=NULL);
    return x;
}

/*
 * 在 logcat 里打印长字符串.
 * logcat 一次只能打印 1024 个字符, 如果字符串太长, 需要多次打印.
 */
void log_long_string (char* buff) {
    char line[1024];
    int length;
    int segment = 0;
    int segment_count;
    int segment_size = 1000;

    length = strlen(buff);
    if (length > 1024) {
        LOGD("[CString] length too long: [%d] > 1024!", length);
        segment_count = (int)ceil(length/(float)segment_size);
        while (segment < segment_count) {
            substring(buff, segment * segment_size, segment_size, line);
            LOGD("%s", line);
            segment++;
        }
    } else {
        // 貌似一次只能输出 1024 字节....
        LOGD("%s", buff);
    }
}

/*
 * 将 unity 里的 string 对象转换为 c 里的 string 对象.
 * @return
 */
char* unity_string_to_native_string(CString *self) {
    if (self->length == 0) {
        return NULL;
    }
    char *buff = (char *)malloc(self->length * 6);
    memset(buff,0,self->length * 6);
    for (int i = 0, off = 0; i < self->length; ++i)
        off += unicode2UTF(((short *)self->start_char)[i], buff + off);
    // 这里我们直接返回了 buff, 所以在外部用完之后一定要手动 free(buff);
    // free(buff);
    return buff;
}

int get_item_value_int(cJSON* item) {
    // LOGD("get item value int...");
    if (cJSON_IsString(item)) {
        return atoi(item->valuestring);
    } else if (cJSON_IsNumber(item)) {
        return item->valueint;
    }
}

void record_drop_info(int reward_kind,int reward_id,int reward_amount,int rarity) {
    if (rarity == 1) {
        treasure_info.r1_total += reward_amount;
    }
    if (rarity == 2) {
        treasure_info.r2_total += reward_amount;
    }
    if (rarity == 3) {
        treasure_info.r3_total += reward_amount;
    }
    std::map<int, Treasure>::iterator it = treasure_map.find(reward_id);
    // treasure_map.end() 返回的是 map 的结尾地址, 这个地址指向的是空元素, 所以这里就是判断元素是否找到
    if (it != treasure_map.end()) {
        Treasure treasure = it->second;
        treasure.item_count++;
        treasure.total += reward_amount;
    } else {
        Treasure treasure;
        treasure.reward_id = reward_id;
        treasure.reward_kind = reward_kind;
        treasure.item_count = 1;
        treasure.total = reward_amount;
        treasure_map.insert(std::pair<int, Treasure>(reward_id, treasure));
    }
}

void parse_drop_info(cJSON* info) {
    // LOGD("parse_drop_info ...");
    cJSON* rewardKind = info->child;
    cJSON* rewardId = rewardKind->next;
    cJSON* rewardAmount = rewardId->next;
    cJSON* treasureBoxId = rewardAmount->next;
    int reward_kind = get_item_value_int(rewardKind);
    int reward_id = get_item_value_int(rewardId);
    int reward_amount = get_item_value_int(rewardAmount);
    int rarity = get_item_value_int(treasureBoxId);

    if (reward_kind == 15 && rarity == 3) { // 大经验书
        record_drop_info(reward_kind, reward_id, reward_amount, rarity);
    } else if (reward_kind == 11) {
        switch (reward_id) {
            case 421: // 种子
            case 301: // 301-303 是石板
            case 302:
            case 303:
                record_drop_info(reward_kind, reward_id, reward_amount, rarity);
        }
    }
}

void parse_treasure_info(char* json) {
    cJSON* treasures = cJSON_Parse(json);
    cJSON* current = treasures->child;
    int i = 0, j = 0;
    while (current) {
        i++;
        // LOGD("enemy %d", i);
        cJSON* current_drop_info = current->child;
        while (current_drop_info) {
            parse_drop_info(current_drop_info);
            current_drop_info = current_drop_info->next;
            j++;
            // LOGD("drop %d", j);
        }
        current = current->next;
    }
    LOGD("r3_total [%d], r2_total [%d], r1_total [%d]", treasure_info.r3_total, treasure_info.r2_total, treasure_info.r1_total);
}

void reset_treasure_info() {
    treasure_info.r1 = 0;
    treasure_info.r2 = 0;
    treasure_info.r3 = 0;
    treasure_info.r1_total = 0;
    treasure_info.r2_total = 0;
    treasure_info.r3_total = 0;

    treasure_map.clear();
}

void get_treasure_info(CString *treasure_json, bool log_info) {
    char* str = unity_string_to_native_string(treasure_json);
    // 注意, 这里 str 有可能是 null, 但是不能用 strlen(null) 来检测, 会报错!!!
    if (str) {
        reset_treasure_info();
        parse_treasure_info(str);
        int r1 = get_substring_count(str, "\"treasureBoxId\":\"1\"");
        int r2 = get_substring_count(str, "\"treasureBoxId\":\"2\"");
        int r3 = get_substring_count(str, "\"treasureBoxId\":\"3\"");
        // 这里记录的是箱子个数
        treasure_info.r1 = r1;
        treasure_info.r2 = r2;
        treasure_info.r3 = r3;
        if (log_info) {
            log_long_string(str);
        }
        LOGD("r3 [%d], r2 [%d], r1 [%d]", r3, r2, r1);
        free(str);
    } else {
        LOGD("Empty [CString].");
    }
}